package com.hntx.joymusic.musicMix;

import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * 目前只能对相同采样率，通道和采样精度的音频进行混音
 * @author Darcy
 */
public abstract class MultiAudioMixer {
	
	private  OnAudioMixListener mOnAudioMixListener;
	
	public static MultiAudioMixer createAudioMixer(){
		return new AverageAudioMixer();
	}
	
	public void setOnAudioMixListener(OnAudioMixListener l){
		this.mOnAudioMixListener = l;
	}
	
	/**
	 * <p>start to mix , you can call {@link #setOnAudioMixListener(OnAudioMixListener)} before this method to get mixed data. 
	 */
	public void mixAudios(File[] rawAudioFiles){
		
		final int fileSize = rawAudioFiles.length;

		FileInputStream[] audioFileStreams = new FileInputStream[fileSize];
		File audioFile = null;
		
		FileInputStream inputStream;
		byte[][] allAudioBytes = new byte[fileSize][];
		boolean[] streamDoneArray = new boolean[fileSize];
		byte[] buffer = new byte[512];
		int offset;
		
		try {
			
			for (int fileIndex = 0; fileIndex < fileSize; ++fileIndex) {
				audioFile = rawAudioFiles[fileIndex];
				audioFileStreams[fileIndex] = new FileInputStream(audioFile);
			}

			while(true){
				
				for(int streamIndex = 0 ; streamIndex < fileSize ; ++streamIndex){
					
					inputStream = audioFileStreams[streamIndex];
					if(!streamDoneArray[streamIndex] && (offset = inputStream.read(buffer)) != -1){
						allAudioBytes[streamIndex] = Arrays.copyOf(buffer,buffer.length);
					}else{
						streamDoneArray[streamIndex] = true;
						allAudioBytes[streamIndex] = new byte[512];
					}
				}
				
				byte[] mixBytes = mixRawAudioBytes(allAudioBytes);
				if(mixBytes != null && mOnAudioMixListener != null){
					mOnAudioMixListener.onMixing(mixBytes);
				}
				
				boolean done = true;
				for(boolean streamEnd : streamDoneArray){
					if(!streamEnd){
						done = false;
					}
				}
				
				if(done){
					if(mOnAudioMixListener != null)
						mOnAudioMixListener.onMixComplete();
					break;
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
			if(mOnAudioMixListener != null)
				mOnAudioMixListener.onMixError(1);
		}finally{
			try {
				for(FileInputStream in : audioFileStreams){
					if(in != null)
						in.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	abstract byte[] mixRawAudioBytes(byte[][] data);
	
	public interface OnAudioMixListener{
		/**
		 * invoke when mixing, if you want to stop the mixing process, you can throw an AudioMixException
		 * @param mixBytes
		 * @throws AudioMixException
		 */
		void onMixing(byte[] mixBytes) throws IOException;
		
		void onMixError(int errorCode);
		
		/**
		 * invoke when mix success
		 */
		void onMixComplete();
	}
	
	public static class AudioMixException extends IOException{
		private static final long serialVersionUID = -1344782236320621800L;
		
		public AudioMixException(String msg){
			super(msg);
		}
	}
	
	/**
	 * 平均值算法
	 * @author Darcy
	 */
	private static class AverageAudioMixer extends MultiAudioMixer{

		@Override
		byte[] mixRawAudioBytes(byte[][] bMulRoadAudioes) {
			
			if (bMulRoadAudioes == null || bMulRoadAudioes.length == 0)
				return null;

			byte[] realMixAudio = bMulRoadAudioes[0];
			
			if(bMulRoadAudioes.length == 1)
				return realMixAudio;
			
			for(int rw = 0 ; rw < bMulRoadAudioes.length ; ++rw){
				if(bMulRoadAudioes[rw].length != realMixAudio.length){
					Log.e("app", "column of the road of audio + " + rw +" is diffrent.");
					return null;
				}
			}
			
			int row = bMulRoadAudioes.length;
			int coloum = realMixAudio.length / 2;
			short[][] sMulRoadAudioes = new short[row][coloum];

			for (int r = 0; r < row; ++r) {
				for (int c = 0; c < coloum; ++c) {
					sMulRoadAudioes[r][c] = (short) ((bMulRoadAudioes[r][c * 2] & 0xff) | (bMulRoadAudioes[r][c * 2 + 1] & 0xff) << 8);
				}
			}

			short[] sMixAudio = new short[coloum];
			int mixVal;
			int sr = 0;
			for (int sc = 0; sc < coloum; ++sc) {
				mixVal = 0;
				sr = 0;
				for (; sr < row; ++sr) {
					mixVal += sMulRoadAudioes[sr][sc];
				}
				sMixAudio[sc] = (short) (mixVal / row);
			}

			for (sr = 0; sr < coloum; ++sr) {
				realMixAudio[sr * 2] = (byte) (sMixAudio[sr] & 0x00FF);
				realMixAudio[sr * 2 + 1] = (byte) ((sMixAudio[sr] & 0xFF00) >> 8);
			}

			return realMixAudio;
		}
		
	}
}
